package com.nulldev.util.web.YouTubeExtractor.formats;

import java.util.List;

import com.nulldev.util.data.Arrays.arrays.FastLists;
import com.nulldev.util.internal.backport.concurrency9.Lists;
import com.nulldev.util.web.YouTubeExtractor.metadata.YouTubeMetadata.DRMMetadata.DRMFamily;

public class Format implements Comparable<Format> {
	private final int itag;
	private final int fps;
	private final VideoCodec videoCodec;
	private final AudioCodec audioCodec;
	private final SubtitleCodec subtitleCodec;
	private final FileType fileType;
	private final int height, bitrate;
	private final boolean isHDR;
	private final boolean isHighAudioChannel;

	public Format(final int itag, final FileType type, final AudioCodec audioCodec, final int bitrate, final boolean isHDR) {
		this(itag, type, -1, -1, VideoCodec.NONE, audioCodec, SubtitleCodec.NONE, bitrate, isHDR, false);
	}

	public Format(final int itag, final FileType type, final AudioCodec audioCodec, final int bitrate, final boolean isHDR, final boolean isHighAudioChannel) {
		this(itag, type, -1, -1, VideoCodec.NONE, audioCodec, SubtitleCodec.NONE, bitrate, isHDR, isHighAudioChannel);
	}

	public Format(final int itag, final FileType type, final int height, final int fps, final VideoCodec videoCodec, final boolean isHDR,
			final boolean isHighAudioChannel) {
		this(itag, type, height, fps, videoCodec, AudioCodec.NONE, SubtitleCodec.NONE, -1, isHDR, isHighAudioChannel);
	}

	public Format(final int itag, final FileType type, final int height, final int fps, final VideoCodec videoCodec, final boolean isHDR) {
		this(itag, type, height, fps, videoCodec, AudioCodec.NONE, SubtitleCodec.NONE, -1, isHDR, false);
	}

	public Format(final int itag, final FileType type, final int height, final int fps, final VideoCodec videoCodec, final boolean isHDR,
			final SubtitleCodec subtitleCodec) {
		this(itag, type, height, fps, videoCodec, AudioCodec.NONE, subtitleCodec, -1, isHDR, false);
	}

	public Format(final int itag, final FileType type, final int fps, final int height, final VideoCodec videoCodec, final AudioCodec audioCodec,
			final int bitrate, final boolean isHDR) {
		this(itag, type, fps, height, videoCodec, audioCodec, SubtitleCodec.NONE, bitrate, isHDR, false);
	}

	public Format(final int itag, final FileType type, final int fps, final int height, final VideoCodec videoCodec, final AudioCodec audioCodec,
			final SubtitleCodec subtitleCodec, final int bitrate, final boolean isHDR, final boolean isHighAudioChannel) {
		this.itag = itag;
		this.fps = fps;
		this.videoCodec = videoCodec;
		this.audioCodec = audioCodec;
		this.isHDR = isHDR;
		this.fileType = type;
		this.height = height;
		this.bitrate = bitrate;
		this.isHighAudioChannel = isHighAudioChannel;
		this.subtitleCodec = subtitleCodec;
	}

	public int getTag() {
		return this.itag;
	}

	public int getFPS() {
		return this.fps;
	}

	public VideoCodec getVideoCodec() {
		return this.videoCodec;
	}

	public boolean hasVideo() {
		return this.videoCodec != null && !this.videoCodec.equals(VideoCodec.NONE);
	}

	public AudioCodec getAudioCodec() {
		return this.audioCodec;
	}

	public SubtitleCodec getSubtitleCodec() {
		return this.subtitleCodec;
	}

	public boolean hasSubtitles() {
		return this.subtitleCodec != null && !this.subtitleCodec.equals(SubtitleCodec.NONE);
	}

	public boolean hasAudio() {
		return this.audioCodec != null && !this.audioCodec.equals(AudioCodec.NONE);
	}

	public boolean isHDR() {
		return this.isHDR;
	}

	public int getHeight() {
		return this.height;
	}

	public int getWidth() {
		return FormatUtils.guessFromHeight(this.height);
	}

	public FileType getFileType() {
		return this.fileType;
	}

	public int getBitrate() {
		return this.bitrate;
	}

	public boolean hasBitrateData() {
		return this.bitrate > 0;
	}

	/**
	 * @returns true if audio channel count is > 2
	 */
	public boolean isHighAudioChannel() {
		return this.isHighAudioChannel;
	}

	private boolean hasDRMProtection;

	protected Format setDRMProtections(final boolean drm) {
		this.hasDRMProtection = drm;
		return this;
	}

	public boolean hasDRMProtection() {
		return this.hasDRMProtection;
	}

	private List<DRMFamily> drmFamilies = FastLists.list();

	public boolean hasDRMFamilies() {
		return this.drmFamilies != null && this.drmFamilies.size() > 0;
	}

	public DRMFamily[] getDRMFamilies() {
		return this.drmFamilies.toArray(new DRMFamily[this.drmFamilies.size()]);
	}

	protected Format addDRMFamilies(final DRMFamily... families) {
		if (families == null || families.length == 0)
			return this;
		if (families.length > 0 && !this.hasDRMProtection)
			this.hasDRMProtection = true;
		this.drmFamilies.addAll(Lists.of(families));
		return this;
	}

	private String trackLang = null;

	public boolean hasTrackLanguage() {
		return this.trackLang != null && !this.trackLang.isEmpty();
	}

	public String getTrackLanguage() {
		return this.trackLang;
	}

	protected Format setTrackLanguage(final String lang) {
		if (lang == null || lang.isEmpty())
			return this;
		this.trackLang = lang;
		return this;
	}

	private String trackID = null;

	public boolean hasTrackID() {
		return this.trackID != null && !this.trackID.isEmpty();
	}

	public String getTrackID() {
		return this.trackID;
	}

	protected Format setTrackID(final String lang) {
		if (lang == null || lang.isEmpty())
			return this;
		this.trackID = lang;
		return this;
	}

	private boolean isDefaultTrack = true;

	public boolean isDefaultTrack() {
		return this.isDefaultTrack;
	}

	public Format setDefaultTrack(final boolean def) {
		this.isDefaultTrack = def;
		return this;
	}

	@Override
	public String toString() {
		return "Format[itag=" + this.itag + ",fileType=" + this.fileType + ",videoCodec=" + this.videoCodec + ",audioCodec=" + this.audioCodec
				+ ",subtitleCodec=" + this.subtitleCodec + ",height=" + this.height + ",fps=" + this.fps + ",bitrate=" + this.bitrate + ",hdr=" + this.isHDR
				+ ",hqAudio=" + this.isHighAudioChannel + ",trackID=" + this.trackID + "]";
	}

	public int getChannels() {
		if (this.isHighAudioChannel)
			return 6;
		else
			return 2;
	}

	@Override
	public int compareTo(final Format o) {
		if (o == null)
			return 0;
		if (this.height < o.height)
			return -1;
		if (this.height > o.height)
			return 1;
		return 0;
	}

	private int liveStreamTargetDuration = -1;

	public boolean hasLiveStreamTargetDuration() {
		return this.liveStreamTargetDuration > 0;
	}

	public int getLiveStreamTargetDuration() {
		return this.liveStreamTargetDuration;
	}

	protected Format setLiveStreamTargetDuration(final int liveStreamTargetDuration) {
		if (liveStreamTargetDuration <= 0)
			return this;
		this.liveStreamTargetDuration = liveStreamTargetDuration;
		return this;
	}

	private boolean isDRC = false;

	public boolean isDRCPresent() {
		return this.isDRC;
	}

	protected Format setDRCPresence(final boolean isDRC) {
		this.isDRC = isDRC;
		return this;
	}

	private boolean isUltraLowQuality = false;

	public boolean isUltraLowQuality() {
		return this.isUltraLowQuality;
	}

	protected Format setUltraLowQuality(final boolean ulq) {
		this.isUltraLowQuality = ulq;
		return this;
	}

	private boolean isEnhancedBitrate = false;

	public boolean isEnhancedBitrate() {
		return this.isEnhancedBitrate;
	}

	protected Format setEnhancedBitrate(final boolean br) {
		this.isEnhancedBitrate = br;
		return this;
	}
}
